package com.ys.day28.socket通信方式;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Objects;
import java.util.concurrent.*;

/**
 * 描述: 全双工通信
 * 全双工（Full Duplex）是通讯传输的一个术语。通信允许数据在两个方向上同时传输，它在能力上相当于两个单工通信方式的结合。
 * 全双工指可以同时（瞬时）进行信号的双向传输（A→B且B→A）。指A→B的同时B→A，是瞬时同步的。
 *
 * @author CodeYang
 * @version 2021/1/8 21:26
 */
public class P3全双工通信 {

    /**
     * 这里可以实现一个 针对 多客户端与单服务器的 全双工通信
     * 一个服务器N个客户端，服务器可以在同一时刻处理多个客户端的请求。
     * 核心思想:利用线程池对象池,无论什么时候创建连接池,其返回的都是一个池对象,所以无论从哪里添加到线程池,都是一个线程池在执行和操作
     *
     * @param args
     */
    public static void main(String[] args) {

        Class<ArrayList> aClass = ArrayList.class;


    }


}

/**
 * * 服务器套接字
 * * 步骤：
 * * 前置条件：定义线程池
 * * 1 创建ServerSocket，并指定端口
 * * 2 轮询监听客户端连接
 * * 3 使用内部类处理客户端请求
 * * 3.1 获取套接字输入和输出管道
 * * 3.2 接收客户端发送的歌曲名称
 * * 3.3 处理请求，根据歌曲名称查询对应的歌词
 * * 3.4 将歌词返回给客户端
 */
class MyServerSocketTest {

    public static void main(String[] args) {
        try (
                // 注册服务
                ServerSocket serverSocket = new ServerSocket(5656)
        ) {
            // 利用多线程模拟实现 多机响应
            ThreadPoolExecutor threadPools = ThreadPools.cretePool();
            // 使用轮询来 监听客户端请求
            while (true) {
                // 外部类负责监听客户端需求
                Socket accept = serverSocket.accept();
                //内部类负责处理客户端需求
                threadPools.execute(new Runnable() {
                    @Override
                    public void run() {
                        try (
                                InputStream inputStream = accept.getInputStream();
                                OutputStream outputStream = accept.getOutputStream();
                                // 字符流包装一下,这里只需要用到字符类型处理
                                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                                BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
                        ) {
                            // 使用输入管道 读请求数据 -> 注意如果客户端没有给结束标志到流中 会一直阻塞杂志这里
                            String name = bufferedReader.readLine();
                            // 使用枚举,根据歌名获得歌词
                            String wordByname = SongEnum.getWordByname(name);
                            // 将歌词内容返回给客户端
                            bufferedWriter.write(wordByname);
                            // 写结束标志
                            bufferedWriter.newLine();
                            bufferedWriter.flush();

                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

/**
 * 客户端套接字
 * * 步骤：
 * * 前置条件：创建一个定时任务的线程池，用于每隔100毫秒向服务器发送请求，模拟多个客户端
 * * 1 创建客户端套接字
 * * 2 获取客户端套接字输入和输出管道
 * * 3 向服务器发送请求(歌曲名称)
 * * 4 获取服务器响应结果
 */
class MyClientSocketTest {

    public static void main(String[] args) {
        // 这里定时任务线程池,本质上还是 与 服务端端里的线程池是一个对象 所以可以模拟实现 全双工的通信方式
        ScheduledExecutorService se = Executors.newScheduledThreadPool(100);
        // 创建本地线程随机数对象
        ThreadLocalRandom random = ThreadLocalRandom.current();
        // 设置定时任务 创建并执行一个周期动作
        se.scheduleAtFixedRate(new Runnable() {
            //匿名内部类 本质上还是一个类

            /**
             * 根据随机数 获得歌词
             * @return
             */
            private String getSonName() {
                // 0-2
                int radon = random.nextInt(3);

                switch (radon) {
                    case 0:
                        return "歌曲1";
                    case 1:
                        return "歌曲2";
                    case 2:
                        return "歌曲3";

                }
                return null;

            }

            /**
             *  线程任务
             */
            @Override
            public void run() {
                try(
                        Socket socket = new Socket("localhost",5656);
                        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                     )
                 {

                     // 先发送请求 歌曲名
                     bufferedWriter.write(getSonName());
                     // 结束标志换行
                     bufferedWriter.newLine();
                     bufferedWriter.flush();
                     // 接收服务器歌词
                     String readLine = bufferedReader.readLine();
                     System.out.println(readLine);

                 }catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, 100, 1000000, TimeUnit.MICROSECONDS);

    }

}


class ThreadPools {

    /**
     * 线程池对象
     */
    private static ThreadPoolExecutor pool = null;

    public static ThreadPoolExecutor cretePool() throws Exception {

        if (null == pool) {
            // 创建线程池
            pool = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MICROSECONDS, new LinkedBlockingQueue<Runnable>(100));

        }
        return pool;

    }
}
